import { packChunk, writeByte8 } from "../lib/utils";
import pako from "pako";
import { CONSTANTS } from "./constants";
import { filter } from "./filters";
import { pngOptionsProps, pngOptions } from "./type";
// import { filterGPU } from "./filtersGPU";
export function drawImage(
  targetBuffer: ArrayBuffer,
  imageData: Uint8ClampedArray,
  width: number,
  height: number,
  targetX: number,
  targetY: number,
  byteWidth: number,
  byteLength: number,
  overlying?: boolean
) {
  const uint8 = new Uint8Array(targetBuffer);
  function calBlend(a1: number, a2: number, c1: number, c2: number) {
    return (c1 * a1 * (1.0 - a2) + c2 * a2) / (a1 + a2 - a1 * a2);
  }

  for (let y = targetY; y < height + targetY; y++) {
    for (let x = targetX; x < width + targetX; x++) {
      const pointer = y * byteWidth * 4 + x * 4;
      if (!overlying) {
        for (let k = 0; k < 4; k++) {
          const pos = pointer + k;
          if (pos < byteLength) {
            uint8[pos] =
              imageData[(y - targetY) * (width * 4) + (x - targetX) * 4 + k];
          }
        }
      } else {
        const [r, g, b, a] = uint8.slice(pointer, pointer + 4);
        const imageStart = (y - targetY) * (width * 4) + (x - targetX) * 4;
        const [or, og, ob, oa] = imageData.slice(imageStart, imageStart + 4);
        const blendA = a + oa - a * oa;
        const blendR = calBlend(oa, a, or, r);
        const blendG = calBlend(oa, a, og, g);
        const blendB = calBlend(oa, a, ob, b);
        uint8[pointer] = blendR;
        uint8[pointer + 1] = blendG;
        uint8[pointer + 2] = blendB;
        uint8[pointer + 3] = blendA;
      }
    }
  }
}
export function createPNG(props: {
  width: number;
  height: number;
  options?: pngOptionsProps;
}) {
  // pako(); //初始化zlib
  const options = getOptions(props);
  const head = [
    createSignature(),
    createHeader(options),
    options.textInfo ? createText(options.textInfo) : null,
    // createGamma(),
  ].filter(Boolean) as ArrayBuffer[];
  const body: ArrayBuffer = new ArrayBuffer(4 * options.width * options.height);
  return {
    head,
    body,
    options,
  };
}
export function bundle(
  head: ArrayBuffer[],
  body: ArrayBuffer,
  options: pngOptions
) {
  const bodyData = filterData(body, options.width, options.height, {
    ...options,
    chunkSize: options.deflateChunkSize,
    level: options.deflateLevel,
    strategy: options.deflateStrategy,
    // gpu: options.gpu,
  });
  const end = packChunk(CONSTANTS.TYPE_IEND);
  return new Blob([...head, bodyData, end], { type: "image/png" });
}
function getOptions({
  width,
  height,
  options,
}: {
  width: number;
  height: number;
  options?: pngOptionsProps;
}) {
  return Object.assign(
    {
      deflateChunkSize: 32 * 1024,
      deflateLevel: 9,
      deflateStrategy: 3,
      bitDepth: 8,
      colorType: 6,
      inputColorType: 6,
      width,
      height,
    },
    options
  );
}
function createSignature() {
  const signature = new ArrayBuffer(8);
  writeByte8(signature, [0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a]);
  return signature;
}
function createHeader(options: pngOptions) {
  const header = new ArrayBuffer(13);
  const view = writeByte8(header, [options.bitDepth, options.colorType], 8);
  view.setUint32(0, options.width);
  view.setUint32(4, options.height);
  return packChunk(CONSTANTS.TYPE_IHDR, header);
}
function createText(text: string) {
  const value = new ArrayBuffer(text.length * 2);
  const dataview = new DataView(value);
  for (let i = 0; i < text.length; i++) {
    dataview.setUint8(i, text[i].charCodeAt(0));
  }
  return packChunk(CONSTANTS.TYPE_tEXt, value);
}
function createGamma() {
  const buffer = new ArrayBuffer(4);
  const view = new DataView(buffer);
  view.setUint32(0, Math.floor(1 * 100000));
  return packChunk(CONSTANTS.TYPE_gAMA, buffer);
}
function filterData(
  data: ArrayBuffer,
  width: number,
  height: number,
  options: any
) {
  let result;
  if (options.gpu) {
    console.log("gpu is disable now, it will be back soon");
  }
  //   result = filterGPU(data, width, height);
  // } else {
  console.log("use cpu");
  result = filter(data, width, height, options);
  // }
  const compressed = pako.deflate(result as any, options);
  //@ts-ignore
  return packChunk(CONSTANTS.TYPE_IDAT, compressed.buffer);
}
